package visitor;

import hypeerweb.Connections;
import hypeerweb.Node;
import hypeerweb.NodeDelegate;
import hypeerweb.WebId;

public abstract class SendVisitor implements Visitor {
	public static String TARGET_KEY = "target";
	private Parameters params;
	public SendVisitor(){
		
	}
	
	/**
	 * Visits the node that is passed in and decides it's next course of action.
	 * 
	 *  @param node
	 *  		the node that is being visited by the visitor
	 *  @param parameters
	 *  		the parameters for the visitor pertaining to its desired goal
	 *  @pre none
	 *  
	 *  @post if the node is to be visited, it will be the post condition of 
	 *  	  intermediateOperation(node, parameters) or targetOperation(node, parameters)
	 */
	public void visit(Node node, Parameters parameters) {
		//The parameter is either storing an Integer or a WebId
		WebId target;
		if(parameters.get(TARGET_KEY) instanceof Integer) {
			target = new WebId((Integer)parameters.get(TARGET_KEY));
		} else {
			target = (WebId)(parameters.get(TARGET_KEY));
		}
		//check that if we arrived at the target node or not.
		if(node.getWebId().getValue() == target.getValue()){
			targetOperation(node,parameters);
		} else{
			//we visit it and then move on to the next closest.
			intermediateOperation(node,parameters);
			
			//this is our current distance from our target
			int dist = node.getWebId().distanceTo(target);
			
			//get all of the connection for that node to find the next closest
			Connections connections = node.getConnections();
			
			Node closest = null;
			
			//check the fold for a closer connection
			if(node.getFold() != null && node.getFold().getWebId().distanceTo(target) < dist){
				closest = node.getFold();
				dist = closest.getWebId().distanceTo(target);
			}
			
			//check all of the connections for a closer distance
			for(NodeDelegate n : connections.getAllConnections()){
				if(n != null && n.getDelegate() != null && n.getDelegate().getWebId().distanceTo(target) < dist) {
					closest = n.getDelegate();
					dist = n.getDelegate().getWebId().distanceTo(target);
				}
			}
			//we didn't find any closer
			if(closest == null){
				System.err.println("Target Node Does Not Exist! Ended on Node: "+node.toString());
			}
			else{
				//go there
				Node nodeToReturn = closest.accept(this, parameters);
				if(nodeToReturn != null) {
					parameters.set("final_node", nodeToReturn);
				}
			}
		}
	}
	
	/**
	 * Creates the parameters for the visitor.
	 * 
	 * @param target
	 * 			 the webid of the target for the operation 
	 * 
	 * @pre params=null
	 * @post params is initialized
	 * 
	 * @return the default parameters for this visitor
	 * 
	 */
	public Parameters createInitialParameters(WebId target){
		params = new Parameters();
		params.set(TARGET, target);
		return params;
	}
	
	/**
	 * This handles the intermediate steps between the start node and target node.
	 * 
	 * @param node
	 * 			 the current node that is being visited
	 * @param parameters
	 * 			 The parameters holding information about where the destination is and what it is supposed to do
	 * @pre none
	 * 
	 * @post a intermediate operation is performed on node
	 * 
	 * 
	 */
	protected abstract void intermediateOperation(Node node, Parameters parameters);
	
	/**
	 * This is what is called when the target node is reached.
	 * 
	 * @param node
	 * 			 the destination node that is currently being visited
	 * @param parameters
	 * 			 The parameters holding information about where the destination is and what it is supposed to do
	 * @pre none
	 * 
	 * @post an operation is preformed on node.
	 * 
	 * 
	 */
	protected abstract void targetOperation(Node node, Parameters parameters);

	public Parameters getParameters() {
		return params;
	}

	public void setParameters(Parameters params) {
		this.params = params;
	}
}
